﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data.Odbc;
using System.Data;
using System.Configuration;
using System.Windows.Forms;

namespace Test2.Model
{
    class DatabaseDSRepository : iDatabaseRepository
    {
        private OdbcConnection conn = new OdbcConnection(ConfigurationSettings.AppSettings["connString"].ToString());
        public DataTable selectUitvoeren(OdbcCommand cmd)
        {
            DataSet ds = new DataSet();
            DataTable dt = new DataTable();
            try
            {
                //if (cmd.Connection.State == ConnectionState.Closed) cmd.Connection.Open();
                //cmd.ExecuteNonQuery();
                //if (cmd.Connection.State == ConnectionState.Open) cmd.Connection.Close();
                OdbcDataAdapter oda = new OdbcDataAdapter();
                oda.SelectCommand = cmd;
                oda.Fill(ds);
                dt = ds.Tables[0];
            }
            #region Exceptionhandlers
            catch (System.Data.Odbc.OdbcException ex)
            {
                string str = "Source:" + ex.Source + "\n" + "Message:" + ex.Message + "\n" + "Stack Trace:" + ex.StackTrace;
                Console.WriteLine(str, "Specific Exception");
                //De user de keuze geven te stoppen
                DialogResult result = DialogResult.Abort;
                result = MessageBox.Show("Er kon geen verbinding worden gemaakt met de database.\n\n" + ex.GetType() + ":\n" + ex.Message,
                                            "Database Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
                if (result == DialogResult.Abort)
                {
                    Application.Exit();
                }
            }
            catch (System.InvalidOperationException ex)
            {
                string str = "Source:" + ex.Source + "\n" + "Message:" + ex.Message + "\n" + "Stack Trace:" + ex.StackTrace;
                Console.WriteLine(str, "Specific Exception");
            }
            catch (System.Data.SqlClient.SqlException ex)
            {
                string str = "Source:" + ex.Source + "\n" + "Message:" + ex.Message;
                Console.WriteLine(str, "Database Exception");
            }
            catch (System.Exception ex)
            {
                string str = "Source:" + ex.Source + "\n" + "Message:" + ex.Message;
                Console.WriteLine(str, "Generic Exception");
            } 
            #endregion
            return dt;
        }

        public void insertUitvoeren(OdbcCommand cmd)
        {
            try
            {
                if (cmd.Connection.State == ConnectionState.Closed) cmd.Connection.Open();
                cmd.ExecuteNonQuery();
                if (cmd.Connection.State == ConnectionState.Open) cmd.Connection.Close();
                //OdbcDataAdapter oda = new OdbcDataAdapter();
                //oda.InsertCommand = cmd;
            }
            #region Exceptionhandlers
            catch (System.Data.Odbc.OdbcException ex)
            {
                string str = "Source:" + ex.Source + "\n" + "Message:" + ex.Message + "\n" + "Stack Trace:" + ex.StackTrace;
                Console.WriteLine(str, "Specific Exception");
                //De user de keuze geven te stoppen
                DialogResult result = DialogResult.Abort;
                result = MessageBox.Show("Er kon geen verbinding worden gemaakt met de database.\n\n" + ex.GetType() + ":\n" + ex.Message,
                                            "Database Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
                if (result == DialogResult.Abort)
                {
                    Application.Exit();
                }
            }
            catch (System.InvalidOperationException ex)
            {
                string str = "Source:" + ex.Source + "\n" + "Message:" + ex.Message + "\n" + "Stack Trace:" + ex.StackTrace;
                Console.WriteLine(str, "Specific Exception");
            }
            catch (System.Data.SqlClient.SqlException ex)
            {
                string str = "Source:" + ex.Source + "\n" + "Message:" + ex.Message;
                Console.WriteLine(str, "Database Exception");
            }
            catch (System.Exception ex)
            {
                string str = "Source:" + ex.Source + "\n" + "Message:" + ex.Message;
                Console.WriteLine(str, "Generic Exception");
            }
            #endregion
        }

        public DataTable getKlanten()
        {
            OdbcCommand cmd = new OdbcCommand("SELECT klantID, voornaam, naam, adres, plaats, postcode, telefoon, email, modify_date FROM dbo.Klanten", conn);
            return selectUitvoeren(cmd);
        }

        /// <summary>
        /// Voert een Select uit op dbo.Klanten om de Klant met de klantID terug te geven.
        /// </summary>
        /// <param name="klantID">Integer met de klantID</param>
        /// <returns>DataTable</returns>
        public DataTable getKlantByID(int klantID)
        {
            OdbcCommand cmd = new OdbcCommand("SELECT klantID, voornaam, naam, adres, plaats, postcode, telefoon, email, modify_date FROM dbo.Klanten WHERE klantID = ?", conn);
            cmd.Parameters.Add("@klantID", OdbcType.Int).Value = klantID;
            return selectUitvoeren(cmd);
        }

        public DataTable getKlantenVoornaamNaam()
        {
            OdbcCommand cmd = new OdbcCommand("SELECT klantID, voornaam, naam FROM dbo.Klanten", conn);
            return selectUitvoeren(cmd);
        }

        public DataTable getKlantenVoornaamNaamFilter(string input)
        {
            input = input.Replace("'", "''");            // anti injection
            OdbcCommand cmd = new OdbcCommand("SELECT klantID, voornaam, naam FROM dbo.Klanten WHERE voornaam LIKE '" + input + "%' OR naam LIKE '" + input + "%'", conn);
            return selectUitvoeren(cmd);
        }

        public DataTable getFacturen()
        {
            OdbcCommand cmd = new OdbcCommand("SELECT factuurID, factuurnummer, datum, opmerkingen FROM dbo.Facturen", conn);
            return selectUitvoeren(cmd);
        }

        public DataTable getFacturenByKlantID(int klantID)
        {
            OdbcCommand cmd = new OdbcCommand("SELECT factuurID, factuurnummer, datum, opmerkingen FROM dbo.Facturen WHERE klantID = ?", conn);
            cmd.Parameters.Add("@klantID", OdbcType.Int).Value = klantID;
            return selectUitvoeren(cmd);
        }

        public DataTable getFactuurDetails()
        {
            OdbcCommand cmd = new OdbcCommand("SELECT factuurDetailID, artikelNaam, prijs, btw FROM dbo.FactuurDetails", conn);
            return selectUitvoeren(cmd);
        }

        public DataTable getFactuurDetailsByFactuurID(int factuurID)
        {
            OdbcCommand cmd = new OdbcCommand("SELECT factuurDetailID, artikelNaam, prijs, btw FROM dbo.FactuurDetails WHERE factuurID = '" + factuurID + "'", conn);
            return selectUitvoeren(cmd);
        }

        public string getLaatsteFactuurnummer()
        {
            OdbcCommand cmd = new OdbcCommand("SELECT TOP 1 factuurnummer FROM dbo.Facturen ORDER BY factuurID DESC", conn);
            if (cmd.Connection.State == ConnectionState.Closed) cmd.Connection.Open();
            string maxnr = (string)cmd.ExecuteScalar();
            if (cmd.Connection.State == ConnectionState.Open) cmd.Connection.Close();
            return maxnr;
        }

        public void insertKlant(string voornaam, string naam, string adres, string plaats, short postcode, string telefoon, string email)
        {
            OdbcCommand cmd = new OdbcCommand("INSERT INTO dbo.Klanten (voornaam, naam, adres, plaats, postcode, telefoon, email) VALUES (?, ?, ?, ?, ?, ?, ?)", conn);
            cmd.Parameters.Add("@voornaam", OdbcType.NVarChar, 50).Value = voornaam;
            cmd.Parameters.Add("@naam", OdbcType.NVarChar, 50).Value = naam;
            cmd.Parameters.Add("@adres", OdbcType.NVarChar, 50).Value = adres;
            cmd.Parameters.Add("@plaats", OdbcType.NVarChar, 50).Value = plaats;
            cmd.Parameters.Add("@postcode", OdbcType.SmallInt).Value = postcode;
            cmd.Parameters.Add("@telefoon", OdbcType.NVarChar, 50).Value = telefoon;
            cmd.Parameters.Add("@email", OdbcType.NVarChar, 50).Value = email;
            insertUitvoeren(cmd);
        }

        /// <summary>
        /// Past een Klant-record aan met de parameters. Gebruikt een Transaction om Concurrency te voorkomen.
        /// Als de modify_date verschilt van die in de db wordt de Update gerollbacked.
        /// </summary>
        /// <param name="klantID">Integer met de klantID van de aan te passen klant.</param>
        /// <param name="modify_date">String met de modify_date van de opgehaalde data.</param>
        /// <returns>bool false als modify_date verschilt.</returns>
        public bool updateKlant(int klantID, string voornaam, string naam, string adres, string plaats, short postcode, string telefoon, string email, byte[] modify_date)
        {
            OdbcCommand cmd = new OdbcCommand("UPDATE dbo.Klanten SET voornaam=?, naam=?, adres=?, plaats=?, postcode=?, telefoon=?, email=? WHERE (klantID=? AND modify_date=?)", conn);
            //SET-parameters
            cmd.Parameters.Add("@voornaam", OdbcType.NVarChar, 50).Value = voornaam;
            cmd.Parameters.Add("@naam", OdbcType.NVarChar, 50).Value = naam;
            cmd.Parameters.Add("@adres", OdbcType.NVarChar, 50).Value = adres;
            cmd.Parameters.Add("@plaats", OdbcType.NVarChar, 50).Value = plaats;
            cmd.Parameters.Add("@postcode", OdbcType.SmallInt).Value = postcode;
            cmd.Parameters.Add("@telefoon", OdbcType.NVarChar, 50).Value = telefoon;
            cmd.Parameters.Add("@email", OdbcType.NVarChar, 50).Value = email;
            //WHERE-parameters
            cmd.Parameters.Add("@klantID", OdbcType.Int).Value = klantID;
            cmd.Parameters.Add("@modify_date", OdbcType.Timestamp).Value = modify_date;

            if (cmd.Connection.State == ConnectionState.Closed) cmd.Connection.Open();
            //begin transaction
            cmd.Transaction = conn.BeginTransaction();
            int rowsAffected = cmd.ExecuteNonQuery();
            bool toegevoegd = false;
            if (rowsAffected == 0)//Niets veranderd aan de db
            {
                cmd.Transaction.Rollback();
                toegevoegd =  false;
            }
            else
            {
                cmd.Transaction.Commit();
                toegevoegd = true;
            }
            cmd.Connection.Close();
            return toegevoegd;
        }

        public void insertFactuur(int klantID, string factuurnummer, DateTime datum, string opmerkingen, List<FactuurDetail> fds)
        {
            OdbcCommand cmd = new OdbcCommand("INSERT INTO dbo.Facturen (klantID, factuurnummer, datum, opmerkingen) VALUES (?, ?, ?, ?); SELECT SCOPE_IDENTITY()", conn);
            cmd.Parameters.Add("@klantID", OdbcType.Int).Value = klantID;
            cmd.Parameters.Add("@factuurnummer", OdbcType.NVarChar, 50).Value = factuurnummer;
            cmd.Parameters.Add("@datum", OdbcType.DateTime).Value = datum;
            cmd.Parameters.Add("@opmerkingen", OdbcType.NVarChar, 50).Value = opmerkingen;
            if (cmd.Connection.State == ConnectionState.Closed) cmd.Connection.Open();
            cmd.Transaction = conn.BeginTransaction();
            int id;
            try
            {
                //insertUitvoeren(cmd); // omdat we SCOPE_IDENTITY moeten returnen schrijven we de code hier i.p.v. insertUitvoeren()
                id = Int32.Parse(cmd.ExecuteScalar().ToString()); // ExecuteScalar() !!! ExecuteNonQuery() gaf verkeerd resultaat...
                cmd.Transaction.Commit();
            }
            catch (OdbcException)
            {
                cmd.Transaction.Rollback();
                //factuurnummer verhogen met 1
                int nieuwFactuurnr = int.Parse(factuurnummer) + 1;
                cmd.Parameters["@factuurnummer"].Value = nieuwFactuurnr + "";
                //opnieuw uitvoeren
                id = Int32.Parse(cmd.ExecuteScalar().ToString());
            }
            if (cmd.Connection.State == ConnectionState.Open) cmd.Connection.Close();
            //OdbcDataAdapter oda = new OdbcDataAdapter();
            //oda.InsertCommand = cmd;
            foreach (FactuurDetail fd in fds)
            {
                insertFactuurDetails(id, fd.artikelNaam.ToString(), (float)fd.prijs, (byte)fd.btw);
            }
        }

        private void insertFactuurDetails(int factuurID, string artikelNaam, float prijs, byte btw)
        {
            OdbcCommand cmd = new OdbcCommand("INSERT INTO dbo.FactuurDetails (factuurID, artikelNaam, prijs, btw) VALUES (?, ?, ?, ?)", conn);
            cmd.Parameters.Add("@factuurID", OdbcType.Int).Value = factuurID;
            cmd.Parameters.Add("@artikelNaam", OdbcType.NVarChar, 50).Value = artikelNaam;
            cmd.Parameters.Add("@prijs", OdbcType.Int).Value = prijs;
            cmd.Parameters.Add("@btw", OdbcType.TinyInt).Value = btw;
            insertUitvoeren(cmd);
        }
    }
}
